﻿using System;
using System.Diagnostics;
using Reflector.CodeModel;

namespace CallGraphExtractor.CecilAdapter
{
    [DebuggerDisplay("{Name}")]
    class TypeDeclaration : ITypeDeclaration
    {
        #region Fields

        private Mono.Cecil.TypeDefinition mTypeDefinition;

        #endregion Fields

        #region Lifecycle

        public TypeDeclaration(Mono.Cecil.TypeDefinition typeDefinitionitem)
        {
            if (typeDefinitionitem == null)
                throw new ArgumentNullException("typeDefinitionitem");
            this.mTypeDefinition = typeDefinitionitem;
        }

        #endregion Lifecycle

        #region Methods

        public override string ToString()
        {
            return this.Name;
        }

        #endregion Methods

        #region Properties

        public bool IsNested
        {
            get { return (mTypeDefinition.IsPublic || mTypeDefinition.IsNotPublic) == false; }
        }

        #endregion Properties

        #region ITypeDeclaration Members

        public bool Abstract
        {
            get { return mTypeDefinition.IsAbstract; }
            set { throw new NotSupportedException(); }//mTypeDefinition.IsAbstract = value;
        }

        public ITypeReference BaseType
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public bool BeforeFieldInit
        {
            get { return mTypeDefinition.IsBeforeFieldInit; }
            set { throw new NotSupportedException(); }//mTypeDefinition.IsBeforeFieldInit = value;
        }

        public IEventDeclarationCollection Events
        {
            get { return new EventDeclarationCollection(mTypeDefinition.Events); }
        }

        public IFieldDeclarationCollection Fields
        {
            get { return new FieldDeclarationCollection(mTypeDefinition.Fields); }
        }

        public bool Interface
        {
            get { return mTypeDefinition.IsInterface; }
            set { throw new NotSupportedException(); }//mTypeDefinition.IsInterface = value; }
        }

        public ITypeReferenceCollection Interfaces
        {
            get { throw new NotImplementedException(); }
        }

        public IMethodDeclarationCollection Methods
        {
            get { return new MethodDeclarationCollection(mTypeDefinition.Methods); ; }
        }

        public ITypeDeclarationCollection NestedTypes
        {
            get { return new TypeDeclarationCollection(mTypeDefinition.NestedTypes); }
        }

        public IPropertyDeclarationCollection Properties
        {
            get { return new PropertyDeclarationCollection(mTypeDefinition.Properties); }
        }

        public bool RuntimeSpecialName
        {
            get { return mTypeDefinition.IsRuntimeSpecialName; }
            set { throw new NotSupportedException(); }//mTypeDefinition.IsRuntimeSpecialName = value; }
        }

        public bool Sealed
        {
            get { return mTypeDefinition.IsSealed; }
            set { throw new NotSupportedException(); }//mTypeDefinition.IsSealed = value; }
        }

        public bool SpecialName
        {
            get { return mTypeDefinition.IsSpecialName; }
            set { throw new NotSupportedException(); }
        }

        /// <summary>
        /// Gets the visibility of the type.
        /// </summary>
        /// <remarks>
        /// Public == public
        /// NestedPublic == public nested in public or private
        /// NestedFamily == protected nested in public or private
        /// NestedAssembly == internal nested in public or private
        /// NestedFamilyOrAssembly == protected internal nested in public or private
        /// NestedFamilyAndAssembly == ?
        /// NestedPrivate == private nested in public or private
        /// Private == private
        /// </remarks>
        public TypeVisibility Visibility
        {
            get
            {
                if (mTypeDefinition.IsNestedAssembly)
                    return TypeVisibility.NestedAssembly;
                else if (mTypeDefinition.IsNestedFamily)
                    return TypeVisibility.NestedFamily;
                else if (mTypeDefinition.IsNestedFamilyAndAssembly)
                    return TypeVisibility.NestedFamilyAndAssembly;
                else if (mTypeDefinition.IsNestedFamilyOrAssembly)
                    return TypeVisibility.NestedFamilyOrAssembly;
                else if (mTypeDefinition.IsNestedPrivate)
                    return TypeVisibility.NestedPrivate;
                else if (mTypeDefinition.IsNestedPublic)
                    return TypeVisibility.NestedPublic;
                else if (mTypeDefinition.IsNotPublic)
                    return TypeVisibility.Private;
                else// if (mTypeDefinition.IsPublic)
                    return TypeVisibility.Public;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region ITypeReference Members

        public ITypeReference GenericType
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public string Name
        {
            get { return mTypeDefinition.Name; }
            set { throw new NotSupportedException(); }
        }

        private string pNamespace;
        public string Namespace
        {
            get
            {
                if (pNamespace == null)
                {
                    if (this.IsNested == true)
                    {
                        var tokens = mTypeDefinition.FullName.Split(new char[] { '/' });
                        tokens[tokens.Length - 1] = "";
                        foreach (var item in tokens)
                        {
                            if (item != "")
                                pNamespace += "." + item;
                        }
                        pNamespace = pNamespace.TrimStart(new char[] { '.' });
                    }
                    else
                        pNamespace = mTypeDefinition.Namespace;
                }
                return pNamespace;
            }
            set { throw new NotSupportedException(); }
        }

        public object Owner
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ITypeDeclaration Resolve()
        {
            throw new NotImplementedException();
        }

        public bool ValueType
        {
            get { return mTypeDefinition.IsValueType; }
            set { throw new NotSupportedException(); }
        }

        #endregion

        #region IComparable Members

        public int CompareTo(object obj)
        {
            TypeDeclaration other = obj as TypeDeclaration;
            if (other == null)
                return 1;
            if (this.Name.Equals(other.Name) && this.Namespace.Equals(other.Namespace))
                return 0;//TODO revies: mi van, ha mindkettő nested?
            else
                return this.Name.CompareTo(other.Name);
        }

        #endregion

        #region IGenericArgumentProvider Members

        public ITypeCollection GenericArguments
        {
            get { return new TypeCollection(mTypeDefinition.GenericParameters); }
        }

        #endregion

        #region IMetadataItem Members

        public int Token
        {
            get { throw new NotImplementedException(); }
        }

        #endregion

        #region ICustomAttributeProvider Members

        public ICustomAttributeCollection Attributes
        {
            get { throw new NotImplementedException(); }
        }

        #endregion

        #region IDocumentationProvider Members

        public string Documentation
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        #endregion
    }
}
